/* Copyright (c) 2022-2025 Qualcomm Technologies International, Ltd. */
/* %%version */

#include "csr_synergy.h"

#ifndef EXCLUDE_GATT_XPAN_CLIENT_MODULE

#include "gatt_xpan_client_write.h"
#include "gatt_xpan_client_debug.h"
#include "gatt_xpan_client_common.h"
#include "gatt_xpan_client_private.h"

extern gatt_xpan_client *xpanClientMain;
/*******************************************************************************/
static void xpanClientControlPointWrite(GXPANC *xpanClient, uint16 size, uint8* buffer)
{
    if (size <= (XPAN_CLIENT_GATT_MTU - 3))
    {
        CsrBtGattWriteCmdReqSend(xpanClient->srvcElem->gattId,
                                 xpanClient->srvcElem->cid,
                                 xpanClient->handles.controlPointHandle,
                                 size,
                                 buffer);
    }
    else
    {
        CsrBtGattWriteReqSend(xpanClient->srvcElem->gattId,
                                 xpanClient->srvcElem->cid,
                                 xpanClient->handles.controlPointHandle,
                                 0, /* offset of first octet to write */
                                 size,
                                 buffer);
    }
}
/*******************************************************************************/
static size_t xpanClientBufferWithLengthEncode(uint8* dest, size_t dest_len, GattXpanClientBufferWithLength* buffWithLen)
{
    *dest++ = buffWithLen->length;
    return SynMemCpyS(dest,
                      dest_len - 1, /* available space is reduced ("-1") to account for the length field written to the first octet */
                      buffWithLen->value,
                      buffWithLen->length) + 1 /* octets written is increased ("+1") to account for the length field written to the first octet */;
}

/*******************************************************************************/
void xpanWriteControlPointConnectSsidReq(GXPANC *xpanClient, GattXpanClientConnectSsidOp* connectSsidOp)
{
    uint8 size = 1 + /* opcode size */
                 1 + /* size of the field holding the SSID length */
                 connectSsidOp->ssid.length +
                 1 + /* size of the field holding the Passphrase length */
                 connectSsidOp->passphrase.length +
                 XPAN_CLIENT_BSSID_LENGTH_OCTETS +
                 2 + /* primaryFrequencyMhz */
                 1 + /* securityMode */
                 XPAN_CLIENT_COUNTRY_STRING_LENGTH_OCTETS;

    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_CONNECT_SSID_OPCODE;
        /*
         *  SSID                Length 1 Octet                  The length of the SSID field
            SSID                SSID Length Octets              The SSID string
         */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &connectSsidOp->ssid);
        /*
            Passphrase          Length 1 Octet                  The length of the Passphrase field
            Passphrase          Passphrase Length Octets        The Passphrase used to authenticate the device to the network
         */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &connectSsidOp->passphrase);
        /*
            BSSID               6 Octets                        BSSID of the Access Point
         */
        SynMemCpyS(dataPtr,
                   lastPtr - dataPtr,
                   &connectSsidOp->bssid[0],
                   XPAN_CLIENT_BSSID_LENGTH_OCTETS);
        dataPtr += XPAN_CLIENT_BSSID_LENGTH_OCTETS;
        /*
            Primary Frequency    2 Octets                        Primary frequency (in MHz whole number only) on which the Access Point is operating.
         */
        *dataPtr++ = (connectSsidOp->primaryFrequencyMhz >> 0) & 0x00FF;
        *dataPtr++ = (connectSsidOp->primaryFrequencyMhz >> 8) & 0x00FF;
        /*
            Security Mode       1 Octet                         0x00 = Open
                                                                0x01 = WEP
                                                                0x02 = WPA
                                                                0x03 = WPA2
                                                                0x04 = Mixed Mode
                                                                0x05 = WPA3
                                                                0x06 = WPA3 transition
                                                                0x07 = Any security mode
                                                                0x08 = Unknown security
                                                                Others = Reserved
         */
        *dataPtr++ = connectSsidOp->securityMode;
        /*
            Country String      3 Octets                        Set to the value contained in the dot11CountryString attribute in IEEE
         */
        *dataPtr++ = connectSsidOp->countryString[0];
        *dataPtr++ = connectSsidOp->countryString[1];
        *dataPtr++ = connectSsidOp->countryString[2];

        pfree(connectSsidOp->ssid.value);
        pfree(connectSsidOp->passphrase.value);

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}
/*******************************************************************************/
void xpanWriteControlPointDisconnectSsidReq(GXPANC *xpanClient, GattXpanClientDisconnectSsidOp* disconnectSsidOp)
{
    uint8 size = 1 + /* opcode size */
                 1 + /* size of the field holding the SSID length */
                 disconnectSsidOp->ssid.length;

    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_DISCONNECT_SSID_OPCODE;
        /*
         *  SSID                Length 1 Octet                  The length of the SSID field
            SSID                SSID Length Octets              The SSID string
         */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &disconnectSsidOp->ssid);
        pfree(disconnectSsidOp->ssid.value);

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}
/*******************************************************************************/
void xpanWriteControlPointConnectedSsidReq(GXPANC *xpanClient, GattXpanClientConnectedSsidOp* connectedSsidOp)
{
    uint8 size = 1 + /* opcode size */
                 1 + /* size of the field holding the SSID length */
                 connectedSsidOp->ssid.length +
                 XPAN_CLIENT_BSSID_LENGTH_OCTETS +
                 2 + /* primaryFrequencyMhz length */
                 XPAN_CLIENT_IPV4_ADDRESS_LENGTH_OCTETS +
                 XPAN_CLIENT_IPV6_ADDRESS_LENGTH_OCTETS +
                 XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS +
                 1 /* size of the Passphrase Length field */ +
                 connectedSsidOp->passphrase.length +
                 1 /* securityMode field */ +
                 XPAN_CLIENT_COUNTRY_STRING_LENGTH_OCTETS;

    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_CONNECTED_SSID_OPCODE;
        /*
         *  SSID                Length 1 Octet                  The length of the SSID field
            SSID                SSID Length Octets              The SSID string
         */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &connectedSsidOp->ssid);
        /*
            BSSID                   6 Octets                BSSID of the Access Point
         */
        dataPtr += SynMemCpyS(dataPtr,
                              lastPtr - dataPtr,
                              &connectedSsidOp->bssid[0],
                              XPAN_CLIENT_BSSID_LENGTH_OCTETS);
        /*
            Primary Frequency        2 Octets                 Primary frequency (in MHz whole number only) on which the Access Point is operating.
         */
        if (dataPtr <= (lastPtr - sizeof(uint16)))
        {
            *dataPtr++ = (connectedSsidOp->primaryFrequencyMhz >> 0) & 0x00FF;
            *dataPtr++ = (connectedSsidOp->primaryFrequencyMhz >> 8) & 0x00FF;
        }
        /*
            IPv4 Address            4 Octets                IPv4 address of the client
         */
        dataPtr += SynMemCpyS(dataPtr,
                              lastPtr - dataPtr,
                              &connectedSsidOp->ipv4Address[0],
                              XPAN_CLIENT_IPV4_ADDRESS_LENGTH_OCTETS);
        /*
            IPv6 Address            16 Octets               IPv6 address of the client
         */
        dataPtr += SynMemCpyS(dataPtr,
                              lastPtr - dataPtr,
                              &connectedSsidOp->ipv6Address[0],
                              XPAN_CLIENT_IPV6_ADDRESS_LENGTH_OCTETS);

        dataPtr += SynMemCpyS(dataPtr,
                              lastPtr - dataPtr,
                              &connectedSsidOp->macAddress[0],
                              XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS);

        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                    lastPtr - dataPtr,
                                                    &connectedSsidOp->passphrase);

        if (dataPtr <= (lastPtr - sizeof(uint8)))
        {
            *dataPtr++ = connectedSsidOp->securityMode;
        }

        dataPtr += SynMemCpyS(dataPtr,
                              lastPtr - dataPtr,
                              &connectedSsidOp->countryString[0],
                              XPAN_CLIENT_COUNTRY_STRING_LENGTH_OCTETS);

        pfree(connectedSsidOp->ssid.value);
        pfree(connectedSsidOp->passphrase.value);

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointAvailableSsidReq(GXPANC *xpanClient, GattXpanClientAvailableSsidOp* availableSsidOp)
{
    uint8 size = 1 + /* opcode size */
                 1 + /* SSID length */
                 availableSsidOp->ssid.length +
                 1 + /* Passphrase length */
                 availableSsidOp->passphrase.length +
                 1 + /* Security mode */
                 1 + /* num BSSIDs field */
                 (availableSsidOp->numBssids * XPAN_CLIENT_BSSID_LENGTH_OCTETS) +
                 (availableSsidOp->numBssids * 2) /* centre frequency */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;
    GattXpanClientBssData *currentBssData;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_AVAILABLE_SSID_OPCODE;
        /*
         *      SSID Length             1 Octets                   The length of the SSID field
                SSID                    SSID Length Octets         The SSID string
         */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &availableSsidOp->ssid);
        /*
                Passphrase Length       1 Octet                    The length of the Passphrase field
                Passphrase              Passphrase Length Octets   The Passphrase used to authenticate the device to the network
         */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &availableSsidOp->passphrase);
        /*
                Security Mode           1 Octet                    0x00 = Open
                                                                   0x01 = WEP
                                                                   0x02 = WPA
                                                                   0x03 = WPA2
                                                                   0x04 = Mixed Mode
                                                                   0x05 = WPA3
                                                                   0x06 = WPA3 transition
                                                                   0x07 = Any security mode
                                                                   0x08 = Unknown security
                                                                   Others = Reserved
         */
        *dataPtr++ = availableSsidOp->securityMode;
        /*
                Num BSSID               1 Octet                    Number of BSSIDs
         */
        *dataPtr++ = availableSsidOp->numBssids;

        for (currentBssData = &availableSsidOp->bssData[0];
             currentBssData < &availableSsidOp->bssData[availableSsidOp->numBssids];
             currentBssData++)
        {
            /*
                    BSSID[i]                6 Octets                   BSSID of the Access Point
             */
            SynMemCpyS(dataPtr,
                       lastPtr - dataPtr,
                       &currentBssData->bssid[0],
                       XPAN_CLIENT_BSSID_LENGTH_OCTETS);
            dataPtr += XPAN_CLIENT_BSSID_LENGTH_OCTETS;
            /*
                    Primary Frequency[i]     2 Octets                   Primary frequency (in MHz whole number only) on which the Access Point is operating.
             */
            *dataPtr++ = (currentBssData->primaryFrequencyMhz >> 0) & 0x00FF;
            *dataPtr++ = (currentBssData->primaryFrequencyMhz >> 8) & 0x00FF;
        }

        pfree(availableSsidOp->bssData);
        pfree(availableSsidOp->passphrase.value);
        pfree(availableSsidOp->ssid.value);

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointRelatedSsidsReq(GXPANC *xpanClient, GattXpanClientRelatedSsidsOp* relatedSsidsOp)
{
    uint16 size;
    uint8* dataPtr;
    uint8* lastPtr;
    uint8* buffer;
    GattXpanClientBufferWithLength* currentSsid;

    size = 1 + /* opcode size */
           1 + /* num SSIDs field */
           (relatedSsidsOp->numSsids * 1) /* N * SSID length fields */;
    /*
     * iterate through SSIDs to get the total length required for the buffer
     */
    for (currentSsid = &relatedSsidsOp->ssid[0];
         currentSsid < &relatedSsidsOp->ssid[relatedSsidsOp->numSsids];
       ++currentSsid)
    {
        size += currentSsid->length;
    }

    buffer = (uint8*)CsrPmemZalloc(size);
    dataPtr = buffer;
    lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_RELATED_SSIDS_OPCODE;
        /*
         Num SSIDs           1 Octet                  Number of SSIDs
         */
        *dataPtr++ = relatedSsidsOp->numSsids;

        for (currentSsid = &relatedSsidsOp->ssid[0];
             currentSsid < &relatedSsidsOp->ssid[relatedSsidsOp->numSsids];
           ++currentSsid)
        {
            /*
            SSID Length[i]      1 Octet                  The length of the SSID[i] field
            SSID[i]             SSID Length[i] Octets    The SSID string
            */
            dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                       lastPtr - dataPtr,
                                                       currentSsid);
            pfree(currentSsid->value);
        }

        pfree(relatedSsidsOp->ssid);

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointMdnsSrvUuidReq(GXPANC *xpanClient, GattXpanClientMdnsSrvUuidOp* mdnsSrvUuidOp)
{
    uint8 size = 1 + /* opcode size */
                 XPAN_CLIENT_MDNS_SRV_UUID_LENGTH_OCTETS;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_MDNS_SRV_UUID_OPCODE;
        /*
        mDNS SRV UUID       16 Octets           128bit UUID. The value should be static for the lifetime of the
                                                bonding between the two devices, unless the value is updated by
                                                the client. This value shall be either randomly generated or
                                                derived using the client's IR as described in Section 3.6.2.
                                                It shall not be in any way derived from any other shared secret
                                                between the two devices (client and server).
        */
        SynMemCpyS(dataPtr,
                   lastPtr - dataPtr,
                   &mdnsSrvUuidOp->mDnsSrvUuid[0],
                   XPAN_CLIENT_MDNS_SRV_UUID_LENGTH_OCTETS);
        dataPtr += XPAN_CLIENT_MDNS_SRV_UUID_LENGTH_OCTETS;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointRemoveSsidReq(GXPANC *xpanClient, GattXpanClientRemoveSsidOp* removeSsidOp)
{
    uint8 size = 1 + /* opcode size */
                 1 + /* SSID Length field */
                 removeSsidOp->ssid.length;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_REMOVE_SSID_OPCODE;
        /*
        SSID Length         1 Octet                 The length of the SSID field
        SSID                SSID Length Octets      The SSID string
        */
        dataPtr += xpanClientBufferWithLengthEncode(dataPtr,
                                                   lastPtr - dataPtr,
                                                   &removeSsidOp->ssid);
        pfree(removeSsidOp->ssid.value);

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointL2capTcpPortReq(GXPANC *xpanClient, GattXpanClientL2capTcpPortOp* l2capTcpPortOp)
{
    uint8 size = 1 + /* opcode size */
                 2   /* L2CAP TCP Port */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_L2CAP_TCP_PORT_OPCODE;
        /*
         * L2CAP TCP Port       2 Octets        TCP port number
         */
        *dataPtr++ = (l2capTcpPortOp->l2capTcpPort >> 0) & 0xFF;
        *dataPtr++ = (l2capTcpPortOp->l2capTcpPort >> 8) & 0xFF;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointUdpPortReq(GXPANC *xpanClient, GattXpanClientUdpPortOp* udpPortOp)
{
    uint8 size = 1 + /* opcode size */
                 2 + /* UDP Port Audio */
                 2   /* UDP Port Reports */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_UDP_PORT_OPCODE;
       /*
        *  UDP Port Audio          2 Octets            UDP port number for sending audio data to the client
        */
        *dataPtr++ = (udpPortOp->udpPortAudio >> 0) & 0xFF;
        *dataPtr++ = (udpPortOp->udpPortAudio >> 8) & 0xFF;
       /*
        *  UDP Port Reports        2 Octets            UDP port number for sending periodic reports to the client
        */
        *dataPtr++ = (udpPortOp->udpPortReports >> 0) & 0xFF;
        *dataPtr++ = (udpPortOp->udpPortReports >> 8) & 0xFF;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointEtherTypeReq(GXPANC *xpanClient, GattXpanClientEtherTypeOp* etherTypeOp)
{
    uint8 size = 1 + /* opcode size */
                 2   /* Ether Type */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_ETHER_TYPE_OPCODE;
        /*
         * Ether Type       2 Octets        EtherType
         */
        *dataPtr++ = (etherTypeOp->etherType >> 0) & 0xFF;
        *dataPtr++ = (etherTypeOp->etherType >> 8) & 0xFF;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointMacAddressReq(GXPANC *xpanClient, GattXpanClientMacAddressOp* macAddressOp)
{
    uint8 size = 1 + /* opcode size */
                 XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_MAC_ADDRESS_OPCODE;
       /*
        * MAC Address      6 Octets        MAC address
        */
        SynMemCpyS(dataPtr,
                   lastPtr - dataPtr,
                   &macAddressOp->macAddress[0],
                   XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS);
        dataPtr += XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointBearerPreferenceResponseReq(GXPANC *xpanClient, GattXpanClientBearerPreferenceResponseOp* bearerPreferenceResponseOp)
{
    uint8 size = 1 + /* opcode size */
                 1   /* status field */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_BEARER_PREFERENCE_RESPONSE_OPCODE;
       /*
        * Status       1 Octet         0x00 = Bearer preference accepted
                                       0x01 = Bearer preference rejected
                                       Others = Reserved
        */
        *dataPtr++ = bearerPreferenceResponseOp->status;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointUpdateBeaconParametersReq(GXPANC *xpanClient, GattXpanClientUpdateBeaconParametersOp* updateBeaconParametersOp)
{
    uint8 size = 1 + /* opcode size */
                 XPAN_CLIENT_TBTT_TSF_LENGTH_OCTETS +
                 2 + /* New beacon interval in milliseconds */
                 2 /* Center Frequency (in MHz; whole number only) */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_UPDATE_BEACON_INTERVAL_OPCODE;
       /*
        * Next TBTT TSF         8 Octets            Time, in microseconds, of the Target Beacon Transmission Time
        *                                           (TBTT) with reference to the Time Synchronization Function
        *                                           (TSF).
        */
        SynMemCpyS(dataPtr,
                   lastPtr - dataPtr,
                   &updateBeaconParametersOp->nextTbttTsfMicroSeconds[0],
                   XPAN_CLIENT_TBTT_TSF_LENGTH_OCTETS);
        dataPtr += XPAN_CLIENT_TBTT_TSF_LENGTH_OCTETS;
        /*
         * Beacon Interval       2 Octets            New beacon interval in milliseconds
         */
        *dataPtr++ = (updateBeaconParametersOp->beaconIntervalMultiplier >> 0) & 0xFF;
        *dataPtr++ = (updateBeaconParametersOp->beaconIntervalMultiplier >> 8) & 0xFF;
        /*
         * Center Frequency       2 Octets            Center Frequency (in MHz; whole number only)
         */
        *dataPtr++ = (updateBeaconParametersOp->primaryFrequency >> 0) & 0xFF;
        *dataPtr++ = (updateBeaconParametersOp->primaryFrequency >> 8) & 0xFF;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointSapPowerStateResponseReq(GXPANC *xpanClient, GattXpanClientSapPowerStateResponseOp* sapPowerStateResponseOp)
{
    uint8 size = 1 + /* opcode size */
                 1   /* status field */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_SAP_POWER_STATE_RESPONSE_OPCODE;
        /*
         * Status               1 Octet                 0x00 = Requested SAP power state accepted
                                                        0x01 = Requested SAP power state rejected
                                                        Others = Reserved
         */
        *dataPtr++ = sapPowerStateResponseOp->status;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointClientFeaturesReq(GXPANC *xpanClient, GattXpanClientClientFeaturesOp* clientFeaturesOp)
{
    uint8 size = 1 + /* opcode size */
                 XPAN_CLIENT_CLIENT_FEATURES_LEN;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_CLIENT_FEATURES_OPCODE;

        SynMemCpyS(dataPtr,
                   lastPtr - dataPtr,
                   &clientFeaturesOp->clientFeatures.value[0],
                   XPAN_CLIENT_CLIENT_FEATURES_LEN);
        dataPtr += XPAN_CLIENT_CLIENT_FEATURES_LEN;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointAudioBearerSwitchRequestReq(GXPANC *xpanClient, GattXpanClientAudioBearerSwitchRequestOp* audioBearerSwitchRequestOp)
{
    uint8 size = 1 + /* opcode size */
                 1   /* size of AudioBearerSwitchRequest */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;

    if (xpanClient != NULL)
    {
        *dataPtr++ = GATT_XPAN_CLIENT_AUDIO_BEARER_SWITCH_REQUEST_OPCODE;

        *dataPtr++ = audioBearerSwitchRequestOp->audioBearerSwitchRequest;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

static bool xpanWriteControlPointGetWiFiScanResultsOpSize(const XpanClientInternalMsgWiFiScanResultsReq* wiFiScanResultsReq,
                                                            size_t* size)
{
    uint8 i;
    /* Validation check */
    if (wiFiScanResultsReq->numBssids > XPAN_CLIENT_WIFI_SCAN_RESULT_NUM_APS_MAX)
    {
        /* The message will not fit into 250 octets (the maximum message size we can send via ATT is 250 octets) */
        return FALSE;
    }

    *size = 1 + /* opcode size */
            8 + /* size of timestamp */
            1 + /* num BSSIDs field */
            wiFiScanResultsReq->numBssids * (
                                                6 + /* size of BSSID */
                                                1 + /* ssid length field */
                                                0 + /* Calculate the number of octets needed for all SSIDs in the next step */
                                                2 + /* channel field */
                                                1 + /* security mode */
                                                1   /* rssi */
                                            );

    /* Calculate the number of octets needed for all SSIDs */
    for (i = 0; i < wiFiScanResultsReq->numBssids; i++)
    {
        if (wiFiScanResultsReq->apScanResult[i])
        {
            /* Validation check - on the client (the test code) allow and SSID len beyond the max, i.e. XPAN_CLIENT_SSID_MAX_LENGTH + 1*/
            if (wiFiScanResultsReq->apScanResult[i]->ssidLength > XPAN_CLIENT_SSID_MAX_LENGTH + 1)
            {
                /* The message is invalid, an SSID value cannot be longer than 32 octets */
                return FALSE;
            }
            if ((wiFiScanResultsReq->apScanResult[i]->ssidLength > 0) &&
                (wiFiScanResultsReq->apScanResult[i]->ssid == NULL))
            {
                /* The message is invalid, an SSID value must be present if the length is greater than zero */
                return FALSE;
            }            /* Increase the size to account for SSID[i] */
            *size += wiFiScanResultsReq->apScanResult[i]->ssidLength;
        }
        else
        {
            /* All 'apScanResult[i]' pointers need to be valid */
            return FALSE;
        }
    }
    return TRUE;
}

void xpanWriteControlPointWiFiScanResultsReq(GXPANC *xpanClient,
                                             XpanClientInternalMsgWiFiScanResultsReq* wiFiScanResultsReq)
{
    int i;
    size_t size;
    uint8* buffer;
    uint8* dataPtr;
    uint8* lastPtr;

    if (xpanClient == NULL)
    {
        xpanFreeContainedPointersWiFiScanResultsReq(wiFiScanResultsReq);
        return;
    }

    /* The xpanWriteControlPointGetWiFiScanResultsOpSize() function needs to perform some validity checks
     * in order to be sure the size calculation is accurate, e.g. it needs to check that:
     *  1. SSIDs are <= 32 octets
     *  2. The number of BSSIDs is <= XPAN_CLIENT_WIFI_SCAN_RESULT_NUM_APS_MAX
     *  3. Contained pointers are valid (not NULL).
     * It needs to do this in order to return a valid size. */
    if (xpanWriteControlPointGetWiFiScanResultsOpSize(wiFiScanResultsReq, &size) == FALSE)
    {
        xpanFreeContainedPointersWiFiScanResultsReq(wiFiScanResultsReq);
        return;
    }

    buffer = (uint8*)CsrPmemZalloc(size);
    dataPtr = buffer;
    lastPtr = buffer + size;
    /*
     * write the opcode
     */
    if (dataPtr <= (lastPtr - sizeof(uint8)))
        *dataPtr++ = GATT_XPAN_CLIENT_WIFI_SCAN_RESULTS_OPCODE;
    /*
     * write the timestamp
     */
    dataPtr += SynMemCpyS(dataPtr,
                          lastPtr - dataPtr,
                          &wiFiScanResultsReq->scanTimestamp[0],
                          XPAN_CLIENT_TSF_TIMESTAMP_LENGTH);
    /*
     * write the number of BSSIDs
     */
    if (dataPtr <= (lastPtr - sizeof(uint8)))
        *dataPtr++ = wiFiScanResultsReq->numBssids;

    for (i = 0; i < wiFiScanResultsReq->numBssids; i++)
    {
        if (wiFiScanResultsReq->apScanResult[i])
        {
            /*
             * Write SSID length[i]
             */
            if (dataPtr <= (lastPtr - sizeof(uint8)))
                *dataPtr++ = wiFiScanResultsReq->apScanResult[i]->ssidLength;
            /*
             * Write the SSID[i] (the length has been validated above)
             */
            if ((wiFiScanResultsReq->apScanResult[i]->ssidLength > 0) &&
                (wiFiScanResultsReq->apScanResult[i]->ssid != NULL))
            {
                dataPtr += SynMemCpyS(dataPtr,
                           lastPtr - dataPtr,
                           &wiFiScanResultsReq->apScanResult[i]->ssid[0],
                           wiFiScanResultsReq->apScanResult[i]->ssidLength);
            }
            /*
             * Write BSSID[i]
             */
            dataPtr += SynMemCpyS(dataPtr,
                                  lastPtr - dataPtr,
                                  &wiFiScanResultsReq->apScanResult[i]->bssid[0],
                                  XPAN_CLIENT_BSSID_LEN);
            /*
             * Write Channel[i]
             */
            if (dataPtr <= (lastPtr - sizeof(uint16)))
            {
                *dataPtr++ = (wiFiScanResultsReq->apScanResult[i]->channelMHz >> 0) & 0xFF;
                *dataPtr++ = (wiFiScanResultsReq->apScanResult[i]->channelMHz >> 8) & 0xFF;
            }
            /*
             * Write Security Mode[i]
             */
            if (dataPtr <= (lastPtr - sizeof(uint8)))
                *dataPtr++ = wiFiScanResultsReq->apScanResult[i]->securityMode;
            /*
             * Write rssi[i]
             */
            if (dataPtr <= (lastPtr - sizeof(uint8)))
                *dataPtr++ = (uint8)wiFiScanResultsReq->apScanResult[i]->rssi;
        }
    }

    /* The wiFiScanResultsReq is a 'complex' message that contains several embedded pointers
     * to dynamically allocated memory. The memory pointed to by 'wiFiScanResultsReq' is freed
     * in the same way that other messages are freed, but the memory pointed to by the embedded
     * pointers is freed here.
     */
    xpanFreeContainedPointersWiFiScanResultsReq(wiFiScanResultsReq);

    xpanClientControlPointWrite(xpanClient,
                                size,
                                buffer);
}

void xpanWriteControlPointChannelSwitchAnnouncementReq(GXPANC* xpanClient,
                                                       GattXpanClientChannelSwitchAnnouncementOp* channelSwitchAnnouncementOp)
{
    uint8 size = 1 + /* opcode size */
                 2 + /* size of primaryFrequencyMHz */
                 8 + /* size of targetTsf */
                 1   /* size of bandwidthMhz*/ ;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient == NULL)
    {
        return;
    }

    if (dataPtr <= (lastPtr - sizeof(uint8)))
        *dataPtr++ = GATT_XPAN_CLIENT_CHANNEL_SWITCH_ANNOUNCEMENT_OPCODE;

    if (dataPtr <= (lastPtr - sizeof(uint16)))
    {
        *dataPtr++ = (channelSwitchAnnouncementOp->primaryFrequencyMHz >> 0) & 0xFF;
        *dataPtr++ = (channelSwitchAnnouncementOp->primaryFrequencyMHz >> 8) & 0xFF;
    }
    /*
     * Write targetTsf
     */
    dataPtr += SynMemCpyS(dataPtr,
                          lastPtr - dataPtr,
                          channelSwitchAnnouncementOp->targetTsf,
                          XPAN_CLIENT_TSF_TIMESTAMP_LENGTH);

    if (dataPtr <= (lastPtr - sizeof(uint8)))
        *dataPtr++ = channelSwitchAnnouncementOp->bandwidthMHz;

    xpanClientControlPointWrite(xpanClient,
                                size,
                                buffer);
}

void xpanWriteControlPointUdpSyncPortReq(GXPANC* xpanClient,
                                         GattXpanClientUdpSyncPortOp* udpSyncPortOp)
{
    uint8 size = 1 + /* opcode size */
                 2   /* size of UdpSyncPortRequest */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        if (dataPtr <= (lastPtr - sizeof(uint8)))
            *dataPtr++ = GATT_XPAN_CLIENT_UDP_SYNC_PORT_OPCODE;

        if (dataPtr <= (lastPtr - sizeof(uint16)))
        {
            *dataPtr++ = (udpSyncPortOp->udpSyncPort >> 0) & 0xFF;
            *dataPtr++ = (udpSyncPortOp->udpSyncPort >> 8) & 0xFF;
        }

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointRoamingRequestReq(GXPANC* xpanClient)
{
    uint8 size = 1 /* opcode size */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        if (dataPtr <= (lastPtr - sizeof(uint8)))
            *dataPtr++ = GATT_XPAN_CLIENT_ROAMING_REQUEST_OPCODE;

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteControlPointMulticastMacAddressReq(GXPANC* xpanClient,
                                                 GattXpanClientMulticastMacAddressOp* multicastMacAddressOp)
{
    uint8 size = 1 + /* opcode size */
                 6   /* size of MulticastMacAddress */;
    uint8* buffer = (uint8*)CsrPmemZalloc(size);
    uint8* dataPtr = buffer;
    uint8* lastPtr = buffer + size;

    if (xpanClient != NULL)
    {
        if (dataPtr <= (lastPtr - sizeof(uint8)))
            *dataPtr++ = GATT_XPAN_CLIENT_MULTICAST_MAC_ADDRESS_OPCODE;

        if (dataPtr <= (lastPtr - XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS))
        {
            SynMemCpyS(dataPtr, XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS,
                       multicastMacAddressOp->multicastMacAddress, XPAN_CLIENT_MAC_ADDRESS_LENGTH_OCTETS);
        }

        xpanClientControlPointWrite(xpanClient,
                                 size,
                                 buffer);
    }
}

void xpanWriteTwtConfigurationRequestReq(GXPANC *xpanClient, GattXpanClientTwtConfiguration* twtConfiguration)
{
    uint8 size;
    uint8* buffer;
    uint8* dataPtr;
    uint8* lastPtr;

    if (xpanClient != NULL)
    {
        if (twtConfiguration != NULL)
        {
            size = 1 + /* Setup ID */
                   1 + /* Flags */
                   4 + /* Desired Wake Duration */
                   4 + /* Desired Wake Interval */
                   GATT_XPAN_CLIENT_READ_TWT_CONFIG_TSF_LEFT_LEN +
                   4   /* Desired Wake TSF Secondary Offset */;
            buffer = (uint8*)CsrPmemZalloc(size);
            dataPtr = buffer;
            lastPtr = buffer + size;

            /*
                Setup ID                          1 Octet             Unique ID representing a TWT session. Nonzero integer.
             */
            *dataPtr++ = twtConfiguration->setupId;

            /*
                Negotiation Type                  1 Bit               0 = Individual TWT
                                                                      1 = Broadcast TWT
                Flow Type                         1 Bit               0 = Announced
                                                                      1 = Unannounced
                Trigger Type                      1 Bit               0 = NonTrigger enabled
                                                                      1 = Trigger enabled
                RFU                               5 Bits              Reserved for future use
             */
            *dataPtr++ = twtConfiguration->flags;

            /*
                Desired Wake Duration             4 Octets            Value in microseconds. May be set to the same value as the
                                                                      Desired Wake Interval field if early termination is used.
             */
            *dataPtr++ = (twtConfiguration->desiredWakeDuration >>  0) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeDuration >>  8) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeDuration >> 16) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeDuration >> 24) & 0xFF;

            /*
                Desired Wake Interval             4 Octets            Value in microseconds. This field shall be set to a valid value.
             */
            *dataPtr++ = (twtConfiguration->desiredWakeInterval >>  0) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeInterval >>  8) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeInterval >> 16) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeInterval >> 24) & 0xFF;

            /*
                Desired Wake TSF Primary          8 Octets            Time, in microseconds, when the first TWT starts for the
                                                                      Primary earbud with reference to the TSF. Set to 0 to indicate
                                                                      that it is not valid in which case the time when the first
                                                                      TWT starts is retrieved from the WiFi subsystem.
             */
            SynMemCpyS(dataPtr,
                       lastPtr - dataPtr,
                       &twtConfiguration->desiredWakeTsfLeft[0],
                       GATT_XPAN_CLIENT_READ_TWT_CONFIG_TSF_LEFT_LEN);
            dataPtr += GATT_XPAN_CLIENT_READ_TWT_CONFIG_TSF_LEFT_LEN;

            /*
                Desired Wake TSF Secondary Offset 4 Octets            Offset, in microseconds, from Desired Wake TSF Primary when
                                                                      the TWT starts for the Secondary earbud. This field shall be
                                                                      set to a valid value.
            */
            *dataPtr++ = (twtConfiguration->desiredWakeTsfRightOffset >>  0) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeTsfRightOffset >>  8) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeTsfRightOffset >> 16) & 0xFF;
            *dataPtr++ = (twtConfiguration->desiredWakeTsfRightOffset >> 24) & 0xFF;

            CsrBtGattWriteCmdReqSend(xpanClient->srvcElem->gattId,
                                     xpanClient->srvcElem->cid,
                                     xpanClient->handles.TwtConfigurationHandle,
                                     size,
                                     buffer);
        }
        else
        {
            /* we are terminating the TWT session (this is done by sending an empty payload) */
            CsrBtGattWriteCmdReqSend(xpanClient->srvcElem->gattId,
                                     xpanClient->srvcElem->cid,
                                     xpanClient->handles.TwtConfigurationHandle,
                                     0,
                                     0);
        }
    }
}

/*******************************************************************************/
void sendXpanClientWriteCfm(GXPANC *const xpan_client,
                                  const status_t status,
                                  GattXpanClientMessageId id)
{
    /* Use GATT_XPAN_CLIENT_GENERIC_WRITE_REQ_CFM to create the message
     * because the structure of all write confirmations is the same,
     * but we will send the right message using the id parameter */
    MAKE_XPAN_CLIENT_MESSAGE(GattXpanClientGenericWriteReqCfm);
    /* Fill in client reference */
    message->clntHndl = xpan_client->srvcElem->service_handle;
    /* Fill in the status */
    message->status = status;

    GATT_XPAN_CLIENT_INFO("GXPANC: Write Cfm handle = 0x%x status = 0x%x msg = 0x%x\n",
                          xpan_client->srvcElem->service_handle, status, id);

    /* Send the confirmation message to app task  */
    XpanClientMessageSend(xpan_client->app_task, id, message);
}


/***************************************************************************/
void xpanClientHandleWriteValueRespCfm(GXPANC *const xpanClient,
                                       const CsrBtGattWriteCfm *const writeCfm)
{
    if (xpanClient != NULL)
    {
        GattXpanClientStatus status = writeCfm->resultCode;

        if(writeCfm->handle == xpanClient->handles.storedSsidCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_STORED_SSID_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if (writeCfm->handle == xpanClient->handles.TwtConfigurationHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_WRITE_TWT_CONFIGURATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.controlPointHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  xpanClient->pending_cmd);

            xpanClient->pending_cmd = 0;
        }
        else if(writeCfm->handle == xpanClient->handles.Ipv4AddressCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_IPV4_ADDRESS_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.Ipv6AddressCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_IPV6_ADDRESS_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.L2capTcpPortCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_L2CAP_TCP_PORT_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.UdpPortCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_UDP_PORT_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.ConnectedDevicesCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_CONNECTED_DEVICES_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.ClearToSendCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_CLEAR_TO_SEND_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.BearerPreferenceCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_BEARER_PREFERENCE_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.MacAddressCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_MAC_ADDRESS_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.RequestedSapPowerStateCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_REQUESTED_SAP_POWER_STATE_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.serverFeaturesCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_SERVER_FEATURES_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.audioBearerSwitchResponseCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_AUDIO_BEARER_SWITCH_RESPONSE_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.numDevicesPresentCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_NUM_DEVICES_PRESENT_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.voiceBackChannelPeriodicityCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_VOICE_BACK_CHANNEL_PERIODICITY_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.requestWiFiScanResultsCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_REQUEST_WIFI_SCAN_RESULTS_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else if(writeCfm->handle == xpanClient->handles.roamingRequestResponseCccHandle)
        {
            sendXpanClientWriteCfm(xpanClient,
                                  status,
                                  GATT_XPAN_CLIENT_ROAMING_REQUEST_RESPONSE_CCC_HANDLE_SET_NOTIFICATION_CFM);
        }
        else
        {
            GATT_XPAN_CLIENT_ERROR("GXPANC: Write Cfm ERROR unknown handle = 0x%x status = 0x%x\n",
                                   writeCfm->handle, status);
        }
    }
    else
    {
        GATT_XPAN_CLIENT_PANIC("Null instance\n");
    }
}

/*******************************************************************************/
void GattXpanClientWriteControlPointConnectSsidReq(const ServiceHandle clntHndl, GattXpanClientConnectSsidOp* connectSsidOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointConnectSsidReq\n");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgConnectSsidReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->connectSsidOp = *connectSsidOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_CONNECT_SSID_OPCODE_REQ,
                              message);
    }
    pfree(connectSsidOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointDisconnectSsidReq(const ServiceHandle clntHndl, GattXpanClientDisconnectSsidOp* disconnectSsidOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointDisconnectSsidReq\n");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgDisconnectSsidReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->disconnectSsidOp = *disconnectSsidOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_DISCONNECT_SSID_OPCODE_REQ,
                              message);
    }
    pfree(disconnectSsidOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointConnectedSsidReq(const ServiceHandle clntHndl, GattXpanClientConnectedSsidOp* connectedSsidOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointConnectedSsidReq\n");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgConnectedSsidReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->connectedSsidOp = *connectedSsidOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_CONNECTED_SSID_OPCODE_REQ,
                              message);
    }
    pfree(connectedSsidOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointAvailableSsidReq(const ServiceHandle clntHndl, GattXpanClientAvailableSsidOp* availableSsidOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointAvailableSsidReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgAvailableSsidReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->availableSsidOp = *availableSsidOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_AVAILABLE_SSID_OPCODE_REQ,
                              message);
    }
    pfree(availableSsidOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointRelatedSsidsReq(const ServiceHandle clntHndl, GattXpanClientRelatedSsidsOp* relatedSsidsOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointRelatedSsidsReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgRelatedSsidsReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->relatedSsidsOp = *relatedSsidsOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_RELATED_SSIDS_OPCODE_REQ,
                              message);
    }
    pfree(relatedSsidsOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointMdnsSrvUuidReq(const ServiceHandle clntHndl, GattXpanClientMdnsSrvUuidOp* mdnsSrvUuidOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointMdnsSrvUuidReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgMdnsSrvUuidReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->mdnsSrvUuidOp = *mdnsSrvUuidOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_MDNS_SRV_UUID_OPCODE_REQ,
                              message);
    }
    pfree(mdnsSrvUuidOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointRemoveSsidReq(const ServiceHandle clntHndl, GattXpanClientRemoveSsidOp* removeSsidOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointRemoveSsidReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgRemoveSsidReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->removeSsidOp = *removeSsidOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_REMOVE_SSID_OPCODE_REQ,
                              message);
    }
    pfree(removeSsidOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointL2capTcpPortReq(const ServiceHandle clntHndl, GattXpanClientL2capTcpPortOp* l2capTcpPortOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointL2capTcpPortReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgL2capTcpPortReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->l2capTcpPortOp = *l2capTcpPortOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_L2CAP_TCP_PORT_OPCODE_REQ,
                              message);
    }
    pfree(l2capTcpPortOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointUdpPortReq(const ServiceHandle clntHndl, GattXpanClientUdpPortOp* udpPortOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointUdpPortReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgUdpPortReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->udpPortOp = *udpPortOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_UDP_PORT_OPCODE_REQ,
                              message);
    }
    pfree(udpPortOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointEtherTypeReq(const ServiceHandle clntHndl, GattXpanClientEtherTypeOp* etherTypeOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointEtherTypeReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgEtherTypeReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->etherTypeOp = *etherTypeOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_ETHER_TYPE_OPCODE_REQ,
                              message);
    }
    pfree(etherTypeOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointMacAddressReq(const ServiceHandle clntHndl, GattXpanClientMacAddressOp* macAddressOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointMacAddressReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgMacAddressReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->macAddressOp = *macAddressOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_MAC_ADDRESS_OPCODE_REQ,
                              message);
    }
    pfree(macAddressOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointBearerPreferenceResponseReq(const ServiceHandle clntHndl, GattXpanClientBearerPreferenceResponseOp* bearerPreferenceResponseOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointBearerPreferenceResponseReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgBearerPreferenceResponseReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->bearerPreferenceResponseOp = *bearerPreferenceResponseOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_BEARER_PREFERENCE_RESPONSE_OPCODE_REQ,
                              message);
    }
    pfree(bearerPreferenceResponseOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointUpdateBeaconParametersReq(const ServiceHandle clntHndl, GattXpanClientUpdateBeaconParametersOp* updateBeaconParametersOp )
{

    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointUpdateBeaconParametersReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgUpdateBeaconParametersReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->updateBeaconParametersOp = *updateBeaconParametersOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_UPDATE_BEACON_PARAMETERS_OPCODE_REQ,
                              message);
    }
    pfree(updateBeaconParametersOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointSapPowerStateResponseReq(const ServiceHandle clntHndl, GattXpanClientSapPowerStateResponseOp* sapPowerStateResponseOp )
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointSapPowerStateResponseReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgSapPowerStateResponseReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->sapPowerStateResponseOp = *sapPowerStateResponseOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_SAP_POWER_STATE_RESPONSE_OPCODE_REQ,
                              message);
    }
    pfree(sapPowerStateResponseOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointClientFeaturesReq(const ServiceHandle clntHndl, GattXpanClientClientFeaturesOp* clientFeaturesOp )
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointClientFeaturesReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgClientFeaturesReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->clientFeaturesOp = *clientFeaturesOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_CLIENT_FEATURES_OPCODE_REQ,
                              message);
    }
    pfree(clientFeaturesOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointAudioBearerSwitchRequestReq(const ServiceHandle clntHndl, GattXpanClientAudioBearerSwitchRequestOp* audioBearerSwtichRequestOp )
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointAudioBearerSwtichRequestReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgAudioBearerSwitchRequestReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->audioBearerSwitchRequestOp = *audioBearerSwtichRequestOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_AUDIO_BEARER_SWITCH_REQUEST_OPCODE_REQ,
                              message);
    }
    pfree(audioBearerSwtichRequestOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointWiFiScanResultsReq(const ServiceHandle clntHndl, GattXpanClientWiFiScanResultsOp* wiFiScanResultsOp)
{
    uint8 i;
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointWiFiScanResultsReq");
        xpanFreeContainedPointersWiFiScanResultsOp(wiFiScanResultsOp);
        return;
    }
    if (wiFiScanResultsOp == NULL)
    {
        GATT_XPAN_CLIENT_WARNING("GXPANC: wiFiScanResultsOp == NULL : GattXpanClientWriteControlPointWiFiScanResultsReq");
        return;
    }
    /* Validation check */
    if (wiFiScanResultsOp->numBssids > XPAN_CLIENT_WIFI_SCAN_RESULT_NUM_APS_MAX)
    {
        /* The message will not fit into 250 octets (the maximum message size we can send via ATT is 250 octets) */
        xpanFreeContainedPointersWiFiScanResultsOp(wiFiScanResultsOp);
        return;
    }
    /* The '-1' in the line below is due to the struct already allocating memory for the first XpanClientApScanResult* */
    MAKE_XPAN_CLIENT_MESSAGE_WITH_LEN(XpanClientInternalMsgWiFiScanResultsReq, (wiFiScanResultsOp->numBssids - 1) * sizeof(XpanClientApScanResult*));
    message->clntHndl = xpanClient->srvcElem->service_handle;
    SynMemCpyS(&message->scanTimestamp[0], XPAN_CLIENT_TSF_TIMESTAMP_LENGTH,
            &wiFiScanResultsOp->scanTimestamp[0], XPAN_CLIENT_TSF_TIMESTAMP_LENGTH);

    message->numBssids = wiFiScanResultsOp->numBssids;

    /* Copy the AP data for all APs in the WiFi Scan Results */
    for (i = 0; i < wiFiScanResultsOp->numBssids; i++)
    {
        /* XpanClientApScanResult* apScanResult = NULL; */
        if (wiFiScanResultsOp->apScanResult[i] == NULL)
        {
            /* All 'apScanResult[i]'s need to be valid */
            xpanFreeContainedPointersWiFiScanResultsReq(message);
            xpanFreeContainedPointersWiFiScanResultsOp(wiFiScanResultsOp);
            pfree(message);
            return;
        }
        /* shallow copy - XPAN Client code must make sure all contained pointers are freed correctly later */
        message->apScanResult[i] = wiFiScanResultsOp->apScanResult[i];
        wiFiScanResultsOp->apScanResult[i] = NULL; /* Prevent the possibility of a double free */
    }

    XpanClientMessageSend(xpanClientMain->lib_task,
                          XPAN_CLIENT_INTERNAL_MSG_WRITE_WIFI_SCAN_RESULTS_OPCODE_REQ,
                          message);
    pfree(wiFiScanResultsOp);
}


/*******************************************************************************/
void GattXpanClientWriteControlPointChannelSwitchAnnouncementReq(const ServiceHandle clntHndl, GattXpanClientChannelSwitchAnnouncementOp* channelSwitchAnnouncementOp)
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointChannelSwitchAnnouncementReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgChannelSwitchAnnouncementReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->channelSwitchAnnouncementOp = *channelSwitchAnnouncementOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_CHANNEL_SWITCH_ANNOUNCEMENT_OPCODE_REQ,
                              message);
    }
    pfree(channelSwitchAnnouncementOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointUdpSyncPortReq(const ServiceHandle clntHndl, GattXpanClientUdpSyncPortOp* udpSyncPortOp)
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointUdpSyncPortReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgUdpSyncPortReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->udpSyncPortOp = *udpSyncPortOp;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_UDP_SYNC_PORT_OPCODE_REQ,
                              message);
    }
    pfree(udpSyncPortOp);
}

/*******************************************************************************/
void GattXpanClientWriteControlPointRoamingRequestReq(const ServiceHandle clntHndl)
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointRoamingRequestReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgRoamingRequestReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_ROAMING_REQUEST_OPCODE_REQ,
                              message);
    }
}

/*******************************************************************************/
void GattXpanClientWriteControlPointMulticastMacAddressReq(const ServiceHandle clntHndl, GattXpanClientMulticastMacAddressOp* multicastMacAddress)
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteControlPointMulticastMacAddressReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgMulticastMacAddressReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->multicastMacAddresOp = *multicastMacAddress;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_MULTICAST_MAC_ADDRESS_OPCODE_REQ,
                              message);
    }

    pfree(multicastMacAddress);
}

/*******************************************************************************/
void GattXpanClientTerminateTwtSession(const ServiceHandle clntHndl)
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientTerminateTwtSession");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgTwtConfigurationReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_TERMINATE_TWT_SESSION_REQ,
                              message);
    }
}

/*******************************************************************************/
void GattXpanClientWriteTwtConfigurationReq(const ServiceHandle clntHndl, GattXpanClientTwtConfiguration* twtConfiguration)
{
    GXPANC *xpanClient = ServiceHandleGetInstanceData(clntHndl);

    if (xpanClient == NULL)
    {
        GATT_XPAN_CLIENT_ERROR("GXPANC: xpanClient == NULL : GattXpanClientWriteTwtConfigurationReq");
    }
    else
    {
        MAKE_XPAN_CLIENT_MESSAGE(XpanClientInternalMsgTwtConfigurationReq);
        message->clntHndl = xpanClient->srvcElem->service_handle;
        message->twtConfiguration = *twtConfiguration;
        XpanClientMessageSend(xpanClientMain->lib_task,
                              XPAN_CLIENT_INTERNAL_MSG_WRITE_TWT_CONFIGURATION_REQ,
                              message);
    }
}

#endif /* EXCLUDE_GATT_XPAN_CLIENT_MODULE */